#ifndef INES_INES_H
#define INES_INES_H

#define True (1)
#define False (0)

#define PPU_VIDEO_WIDTH (0x100)
#define PPU_VIDEO_HEIGHT (0xF0)

typedef char byte;
typedef char *String;
typedef long long int64;
typedef unsigned char bool;
typedef unsigned char uint8;
typedef unsigned int uint32;
typedef unsigned short uint16;
typedef unsigned long long uint64;

typedef struct CPUPrivate CPU;
typedef struct PPUPrivate PPU;
typedef struct MemBusPrivate MemBus;
typedef struct CartridgeMapperPrivate CartridgeMapper;

typedef void (*InesGameCallback)(const uint8 *pixelBuf);

typedef enum {
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL
} Level;


typedef enum {
    NTSC,
    PAL
} NesRegion;

typedef enum {
    INES,
    NES_20
} NesFormat;

typedef enum {
    NMI,
    IRQ,
    BRK,
    RESET,
} ExternalIRQ;

typedef enum {
    HORIZONTAL,
    VERTICAL,
    SINGLE_LOWER,
    SINGLE_UPPER,
    FOR_SCREEN
} Mirroring;

typedef enum {
    NROM,
    MMC1,
    UXROM,
    CNROM,
    UN_SUPPORT
} Mapper;


typedef struct NesConsolePrivate NesConsole;

typedef enum {
    //Generate result in this error due to file 0-3 byte not equal 'nes' character
    UNKNOWN_ROM_FILE_FORMAT = 1000,
    UNKNOWN_CARTRIDGE_BRAND,
} ErrorCode;


static const uint8 DEF_PPU_PALETTE[][3] = {
        {0x80, 0x80, 0x80},
        {0x00, 0x3D, 0xA6},
        {0x00, 0x12, 0xB0},
        {0x44, 0x00, 0x96},
        {0xA1, 0x00, 0x5E},
        {0xC7, 0x00, 0x28},
        {0xBA, 0x06, 0x00},
        {0x8C, 0x17, 0x00},
        {0x5C, 0x2F, 0x00},
        {0x10, 0x45, 0x00},
        {0x05, 0x4A, 0x00},
        {0x00, 0x47, 0x2E},
        {0x00, 0x41, 0x66},
        {0x00, 0x00, 0x00},
        {0x05, 0x05, 0x05},
        {0x05, 0x05, 0x05},
        {0xC7, 0xC7, 0xC7},
        {0x00, 0x77, 0xFF},
        {0x21, 0x55, 0xFF},
        {0x82, 0x37, 0xFA},
        {0xEB, 0x2F, 0xB5},
        {0xFF, 0x29, 0x50},
        {0xFF, 0x22, 0x00},
        {0xD6, 0x32, 0x00},
        {0xC4, 0x62, 0x00},
        {0x35, 0x80, 0x00},
        {0x05, 0x8F, 0x00},
        {0x00, 0x8A, 0x55},
        {0x00, 0x99, 0xCC},
        {0x21, 0x21, 0x21},
        {0x09, 0x09, 0x09},
        {0x09, 0x09, 0x09},
        {0xFF, 0xFF, 0xFF},
        {0x0F, 0xD7, 0xFF},
        {0x69, 0xA2, 0xFF},
        {0xD4, 0x80, 0xFF},
        {0xFF, 0x45, 0xF3},
        {0xFF, 0x61, 0x8B},
        {0xFF, 0x88, 0x33},
        {0xFF, 0x9C, 0x12},
        {0xFA, 0xBC, 0x20},
        {0x9F, 0xE3, 0x0E},
        {0x2B, 0xF0, 0x35},
        {0x0C, 0xF0, 0xA4},
        {0x05, 0xFB, 0xFF},
        {0x5E, 0x5E, 0x5E},
        {0x0D, 0x0D, 0x0D},
        {0x0D, 0x0D, 0x0D},
        {0xFF, 0xFF, 0xFF},
        {0xA6, 0xFC, 0xFF},
        {0xB3, 0xEC, 0xFF},
        {0xDA, 0xAB, 0xEB},
        {0xFF, 0xA8, 0xF9},
        {0xFF, 0xAB, 0xB3},
        {0xFF, 0xD2, 0xB0},
        {0xFF, 0xEF, 0xA6},
        {0xFF, 0xF7, 0x9C},
        {0xD7, 0xE8, 0x95},
        {0xA6, 0xED, 0xAF},
        {0xA2, 0xF2, 0xDA},
        {0x99, 0xFF, 0xFC},
        {0xDD, 0xDD, 0xDD},
        {0x11, 0x11, 0x11},
        {0x11, 0x11, 0x11}
};


/**
 * Debug level is enable
 *
 */
extern bool ines_debug_enable();

/**
 * Output debug log
 */
extern void ines_debug(String text, ...);

/**
 * Output info log
 */
extern void ines_info(String text, ...);

/**
 * Output warn log
 */
extern void ines_warn(String text, ...);

/**
 * output error log
 */
extern void ines_error(String text, ...);

/**
 * Output fatal log and exit application
 */
extern void ines_fatal(String text, ...);


extern String ines_error_text(int err_code);

/**
 * Use nes rom instance nes console object
 * @param buf Nes rom
 * @param length Nes rom length
 */
extern NesConsole *ines_console_new(uint8 *buf, InesGameCallback callback);

/**
 * Dispose NesConsole instance
 */
extern void ines_console_dispose(NesConsole **console);

/**
 * Execute nes console
 *
 * @return Next instruction consumer cpu cycle
 */
extern uint32 ines_console_execute(NesConsole *console);

/**
 * Get cpu master cycle
 */
extern uint64 ines_console_master_cycle(NesConsole *console);

#endif //INES_INES_H
